package com.nus.fellowmark.common.mongo.service;

import com.mongodb.BasicDBObject;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.nus.fellowmark.common.mongo.model.LoadFile;
import org.apache.commons.io.IOUtils;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsOperations;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author Ruoyi Chen
 * @email chenruoyisz@gmail.com
 * @create 2023/2/17
 */
@Service
public class MongoFileServiceImpl implements MongoFileService {
    @Autowired
    private GridFsTemplate template;

    @Autowired
    private GridFsOperations operations;


    @Override
    public String uploadFileWithName(MultipartFile file, String fileName) {
        BasicDBObject metadata = new BasicDBObject();
        metadata.put("filesize", file.getSize());
        metadata.put("bucketName", "test");

        ObjectId fileId = null;
        try {
            fileId = template.store(file.getInputStream(), fileName, file.getContentType(), metadata);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return fileId.toString();
    }


    @Override
    public String uploadFile(MultipartFile file) {
        return uploadFileWithName(file, file.getOriginalFilename());
    }

    @Override
    public LoadFile downloadFile(String id) {
        GridFSFile gridFsFile = template.findOne(new Query(Criteria.where("_id").is(id)));
        if (gridFsFile == null) {
            return null;
        }

        LoadFile loadFile = new LoadFile();

        if (gridFsFile.getMetadata() != null) {
            loadFile.setFilename(gridFsFile.getFilename());
            loadFile.setFileType(gridFsFile.getMetadata().get("_contentType").toString());
            loadFile.setFileSize(gridFsFile.getMetadata().get("filesize").toString());
            try {
                loadFile.setInputStream(operations.getResource(gridFsFile).getInputStream());
                loadFile.setFile(IOUtils.toByteArray(operations.getResource(gridFsFile).getInputStream()));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return loadFile;
    }

    @Override
    public Boolean deleteFile(String id) {
        try {
            template.delete(new Query(Criteria.where("_id").is(id)));
        } catch (Exception e) {
            e.printStackTrace();
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    @Override
    public void downloadFilesAsZip(List<String> ids, HttpServletResponse response) throws IOException {
        List<ObjectId> idList = ids.stream().map(ObjectId::new).collect(Collectors.toList());
        Query query = new Query(Criteria.where("_id").in(idList));
        List<GridFSFile> fileList = new ArrayList<>();
        template.find(query).into(fileList);

        HashSet<String> set = new HashSet<>();

        //if fileList is not empty, loop through the list
        if (fileList.size() > 0) {

            //create a zip file
            ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());

            for (GridFSFile gridFSFile : fileList) {
                //file id is returning as a bson value
                BsonValue bsonValue = gridFSFile.getId();
                String fileId = String.valueOf(bsonValue.asObjectId().getValue());
                String filename = gridFSFile.getFilename();
                if (set.contains(filename)) {
                    int idx = filename.lastIndexOf(".");
                    String postfix = filename.substring(idx);
                    String name = filename.substring(0, idx);
                    filename = name + "-" + UUID.randomUUID().toString().substring(0,5) + postfix;
                }
                set.add(filename);


                //find and retrieve file (using previous download method)
                LoadFile file = downloadFile(fileId);
                file.setFilename(filename);

                //add file to the zip file entry
                ZipEntry zipEntry = new ZipEntry(file.getFilename());
                zipEntry.setSize(Long.parseLong(file.getFileSize()));

                zipOutputStream.putNextEntry(zipEntry);

                ByteArrayResource fileResource = new ByteArrayResource(file.getFile());
                StreamUtils.copy(fileResource.getInputStream(), zipOutputStream);

                zipOutputStream.closeEntry();
            }

            zipOutputStream.finish();
            zipOutputStream.close();
        }

    }


}
